/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

#ifndef __DOCUMENT_HDR__
#define __DOCUMENT_HDR__

#ifdef WIN32
	// disable 'identifier' truncation warnings
	#pragma warning(disable:4786)
#endif


#include <map>
#include <stdexcept>
#include <string>
#include <vector>


namespace xml{

	/*!
	 * \struct document_error
	 */
	struct document_error : public std::runtime_error{
		document_error(const std::string& msg) 
			:std::runtime_error(msg){}
	};


	/*!
	 * \class NodeHandler
	 */
	class NodeHandler;

	
	/*!
	 * \struct DocumentNode
	 * 
	 * Note:
	 * - children implemented as vector since order is important 
	 *   (xml sequence)
	 * - tried multimap which gives good lookup but has issues
	 *   + the returning of items with same key in insertion order is 
	 *     NON standard
	 * - considered map<name, vector>
	 *   + overcomes multimap same key elements order issues
	 *   + sometimes (eg. sector descriptors) different types may need
	 *     to be accessed in insertion order.
	 */
	struct DocumentNode	{

		DocumentNode( const std::string & );
		~DocumentNode();

		std::string type;
		std::string idx;
		std::map<std::string, std::string> attributes;
		std::string content;
		DocumentNode* parent;
		std::vector< DocumentNode* > children;
	};


	/*!
	 * \class Document document.h
	 */
	class Document
	{
	public:

		Document();
		virtual ~Document();

	public:

		virtual void createChild(const std::string&);

	public:
		
		void ascendParent();

		void setReference(const std::string&);
		void setIDRef(const std::string&);
		
		void addAttribute(const std::string&, const std::string&);
		void setContent(const std::string &);

		void setWarning(){ ++_errors; }
		void setError(){ ++_errors; }
		void setFatal(){ ++_errors; }

		bool is_valid()const{ return _valid; }

	public:

		void process_children(
			const DocumentNode*,
			NodeHandler&
		)const;

		// lookup functions
		//
		// - could move these to DocNode *BUT* trying
		//   to keep these light. Also 'getReferencedNode'
		//   better in doc class

		const DocumentNode* root()const{ return _root; }
		const DocumentNode* current()const{ return _current; }

		const DocumentNode* getReferencedNode( const std::string & ) const;

		bool exists( const DocumentNode *, const std::string & ) const;
		const DocumentNode* getDescendant( const DocumentNode *, const std::string & ) const;
		const DocumentNode* hasDescendant( const DocumentNode *, const std::string & ) const;
		bool attributeExists( const DocumentNode *, const std::string & ) const;
		std::string getAttributeValue( const DocumentNode *, const std::string & ) const;
		int getIntegerAttribute( const DocumentNode *, const std::string & ) const;
		int getDoubleAttribute( const DocumentNode *, const std::string & ) const;
		bool getBooleanAttribute( const DocumentNode *, const std::string &	) const;
		std::string getStringContent( const DocumentNode *, const std::string & = "" ) const;

	public: // virtual

		virtual bool validate();

		virtual int getIntegerContent(
			const DocumentNode*, const std::string& = ""
		)const;

		virtual double getDecimalContent(
			const DocumentNode*, const std::string& =""
		)const;

		virtual float getFloatContent(
			const DocumentNode*, const std::string& =""
		)const;

	protected:

		int _errors;
		bool _valid;

		DocumentNode *_root;
		DocumentNode *_current;
		
		std::map<std::string, DocumentNode*> _references;
	};


	/*!
	 * \class has_type
	 */
	class has_type
	{
	public:

		has_type(const std::string& type):_type(type){}
		~has_type(){}

		bool operator()(const DocumentNode* node){
			return _type == node->type;
		}

	private:

		std::string _type;

	};


	/*!
	 * \struct pact::DocumentView
	 */
	struct DocumentView{
		virtual ~DocumentView(){}
	};


	/*!
	 * \class NodeHandler
	 */
	class NodeHandler
	{
	public:

		NodeHandler(const Document* doc, DocumentView* view)
			:_doc(doc), _view(view){}
		virtual ~NodeHandler(){}

		virtual void operator()(const DocumentNode*) = 0;
		virtual void done() {};

	protected:

		const Document* _doc;
		DocumentView* _view;

	};


	/*!
	 * \typedef 
	 */
	typedef std::vector<DocumentNode*> DocNodes;
	typedef DocNodes::const_iterator ChildCIt;

	typedef std::map<std::string, std::string> AttributeMap;
	typedef AttributeMap::const_iterator AttribMapCIt;

	typedef std::map<std::string, DocumentNode*> ReferenceMap;
	typedef ReferenceMap::const_iterator RefMapCIt;


	
}; 

#endif 
